package com.d3works.apps.businessproject.service;

import com.d3works.apps.account.db.Invoice;
import com.d3works.apps.account.db.InvoiceLine;
import com.d3works.apps.base.db.PriceList;
import com.d3works.apps.base.db.Product;
import com.d3works.apps.base.service.PriceListService;
import com.d3works.apps.base.service.ProductCompanyService;
import com.d3works.apps.hr.db.Employee;
import com.d3works.apps.hr.db.Timesheet;
import com.d3works.apps.hr.db.TimesheetLine;
import com.d3works.apps.hr.db.repo.TimesheetLineRepository;
import com.d3works.apps.hr.db.repo.TimesheetRepository;
import com.d3works.apps.hr.service.app.AppHumanResourceService;
import com.d3works.apps.hr.service.config.HRConfigService;
import com.d3works.apps.hr.service.timesheet.TimesheetLineService;
import com.d3works.apps.hr.service.timesheet.TimesheetServiceImpl;
import com.d3works.apps.hr.service.user.UserHrService;
import com.d3works.apps.message.service.TemplateMessageService;
import com.d3works.apps.project.db.Project;
import com.d3works.apps.project.db.ProjectPlanningTime;
import com.d3works.apps.project.db.repo.ProjectPlanningTimeRepository;
import com.d3works.apps.project.db.repo.ProjectRepository;
import com.d3works.apps.project.db.repo.ProjectTaskRepository;
import com.d3works.apps.project.service.ProjectService;
import com.d3works.auth.db.repo.UserRepository;
import com.d3works.common.ObjectUtils;
import com.d3works.exception.D3worksException;
import com.google.inject.Inject;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class TimesheetProjectServiceImpl extends TimesheetServiceImpl
    implements TimesheetProjectService {

  @Inject
  public TimesheetProjectServiceImpl(
      PriceListService priceListService,
      AppHumanResourceService appHumanResourceService,
      HRConfigService hrConfigService,
      TemplateMessageService templateMessageService,
      ProjectRepository projectRepo,
      UserRepository userRepo,
      UserHrService userHrService,
      TimesheetLineService timesheetLineService,
      ProjectPlanningTimeRepository projectPlanningTimeRepository,
      ProjectTaskRepository projectTaskRepo,
      ProductCompanyService productCompanyService,
      TimesheetLineRepository timesheetlineRepo,
      TimesheetRepository timeSheetRepository,
      ProjectService projectService) {
    super(
        priceListService,
        appHumanResourceService,
        hrConfigService,
        templateMessageService,
        projectRepo,
        userRepo,
        userHrService,
        timesheetLineService,
        projectPlanningTimeRepository,
        projectTaskRepo,
        productCompanyService,
        timesheetlineRepo,
        timeSheetRepository,
        projectService);
  }

  @Override
  public List<InvoiceLine> createInvoiceLines(
      Invoice invoice, List<TimesheetLine> timesheetLineList, int priority)
      throws D3worksException {

    if (!appHumanResourceService.isApp("business-project")) {
      return super.createInvoiceLines(invoice, timesheetLineList, priority);
    }

    List<InvoiceLine> invoiceLineList = new ArrayList<InvoiceLine>();
    int count = 0;
    DateTimeFormatter ddmmFormat = DateTimeFormatter.ofPattern("dd/MM");
    HashMap<String, Object[]> timeSheetInformationsMap = new HashMap<String, Object[]>();
    // Check if a consolidation by product and user must be done
    boolean consolidate = appHumanResourceService.getAppTimesheet().getConsolidateTSLine();

    for (TimesheetLine timesheetLine : timesheetLineList) {
      Object[] tabInformations = new Object[6];
      Product product = timesheetLine.getProduct();
      tabInformations[0] = product;
      tabInformations[1] = timesheetLine.getEmployee();
      // Start date
      tabInformations[2] = timesheetLine.getDate();
      // End date, useful only for consolidation
      tabInformations[3] = timesheetLine.getDate();
      tabInformations[4] =
          timesheetLine.getDurationForCustomer() != null
              ? this.computeDurationForCustomer(timesheetLine)
              : timesheetLine.getHoursDuration();
      tabInformations[5] = timesheetLine.getProject();

      String key = null;
      if (consolidate) {
        key =
            (product != null ? product.getId() + "|" : "")
                + timesheetLine.getEmployee().getId()
                + "|"
                + timesheetLine.getProject().getId();
        if (timeSheetInformationsMap.containsKey(key)) {
          tabInformations = timeSheetInformationsMap.get(key);
          // Update date
          if (timesheetLine.getDate().compareTo((LocalDate) tabInformations[2]) < 0) {
            // If date is lower than start date then replace start date by this one
            tabInformations[2] = timesheetLine.getDate();
          } else if (timesheetLine.getDate().compareTo((LocalDate) tabInformations[3]) > 0) {
            // If date is upper than end date then replace end date by this one
            tabInformations[3] = timesheetLine.getDate();
          }
          tabInformations[4] =
              ((BigDecimal) tabInformations[4])
                  .add(
                      timesheetLine.getDurationForCustomer() != null
                          ? this.computeDurationForCustomer(timesheetLine)
                          : timesheetLine.getHoursDuration());
        } else {
          timeSheetInformationsMap.put(key, tabInformations);
        }
      } else {
        key = String.valueOf(timesheetLine.getId());
        timeSheetInformationsMap.put(key, tabInformations);
      }
    }

    for (Object[] timesheetInformations : timeSheetInformationsMap.values()) {

      String strDate = null;
      Product product = (Product) timesheetInformations[0];
      Employee employee = (Employee) timesheetInformations[1];
      LocalDate startDate = (LocalDate) timesheetInformations[2];
      LocalDate endDate = (LocalDate) timesheetInformations[3];
      BigDecimal hoursDuration = (BigDecimal) timesheetInformations[4];
      Project project = (Project) timesheetInformations[5];
      PriceList priceList = project.getPriceList();
      if (consolidate) {
        if (startDate != null && endDate != null) {
          strDate = startDate.format(ddmmFormat) + " - " + endDate.format(ddmmFormat);
        }
      } else {
        if (startDate != null) {
          strDate = startDate.format(ddmmFormat);
        }
      }

      invoiceLineList.addAll(
          this.createInvoiceLine(
              invoice,
              product,
              employee,
              strDate,
              hoursDuration,
              priority * 100 + count,
              priceList));
      invoiceLineList.get(invoiceLineList.size() - 1).setProject(project);
      count++;
    }

    return invoiceLineList;
  }

  @Override
  public BigDecimal computeDurationForCustomer(TimesheetLine timesheetLine)
      throws D3worksException {
    return timesheetLineService.computeHoursDuration(
        timesheetLine.getTimesheet(), timesheetLine.getDurationForCustomer(), true);
  }

  @Override
  protected TimesheetLine createTimeSheetLineFromPPT(
      Timesheet timesheet, ProjectPlanningTime projectPlanningTime) throws D3worksException {
    TimesheetLine line = super.createTimeSheetLineFromPPT(timesheet, projectPlanningTime);
    if (ObjectUtils.notEmpty(projectPlanningTime.getProjectTask())
        && projectPlanningTime.getProjectTask().getInvoicingType()
            == ProjectTaskRepository.INVOICING_TYPE_TIME_SPENT) {
      line.setToInvoice(projectPlanningTime.getProjectTask().getToInvoice());
    }
    return line;
  }
}
